// --------------------------------------------------------------------------------------------------------------------- 
// <copyright file="TransitionProcessor.cs" company="Scrum for Team System">
//   None
// </copyright>
// <summary>
// Defines the TransitionProcessor type.
// </summary>
// ---------------------------------------------------------------------------------------------------------------------
namespace ScrumForTeamSystem.EventService.TransitionService
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.Linq;

    using DataStructures;

    using Infrastructure;
    using Infrastructure.Interfaces;

    using Microsoft.TeamFoundation.WorkItemTracking.Client;

    using ScrumForTeamSystem.EventService.Services;
    using ScrumForTeamSystem.EventService.TransitionService.Properties;

    /// <summary>
    /// Defines the TransitionProcessor type
    /// </summary>
    [Export(typeof(ITfsEventProcessor))]
    public class TransitionProcessor : WorkItemChangedEventProcessorBase<TransitionRule>
    {
        /// <summary>
        /// The updater instance.
        /// </summary>
        private readonly Updater updater;

        /// <summary>
        /// Initializes a new instance of the <see cref="TransitionProcessor"/> class.
        /// </summary>
        public TransitionProcessor() : this(DependencyResolver.GetImplementation<IWorkItemStoreRepository>())
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TransitionProcessor"/> class.
        /// </summary>
        /// <param name="workItemStore">
        /// The wi store.
        /// </param>
        public TransitionProcessor(IWorkItemStoreRepository workItemStore) : base(workItemStore)
        {
            this.updater = new Updater();
            this.updater.ProcessMessage += s => this.OnMessage(this.updater, s);
        }

        /// <summary>
        /// Loads the rules.
        /// </summary>
        /// <returns>A collection of rules.</returns>
        protected override IRulesCollection<TransitionRule> LoadRules()
        {
            var rules = ResourceHelper.LoadRules();

            this.OnMessage(this, Resources.String001);

            return rules;
        }

        /// <summary>
        /// Processes the link changes.
        /// </summary>
        /// <param name="workItemChangedEvent">The work item changed event.</param>
        /// <param name="workItemStore">The work item store.</param>
        /// <param name="allRules">All rules.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1", Justification = "Validated in assert method")]
        protected override void ProcessLinkChanges(WorkItemChangedEvent workItemChangedEvent, WorkItemStore workItemStore, IEnumerable<TransitionRule> allRules)
        {
            AssertEventAndStoreAreNotNull(workItemChangedEvent, workItemStore);

            var allTransitionRules = allRules as TransitionRulesCollection;

            if (allTransitionRules == null)
            {
                throw new ArgumentException(Resources.String010);
            }

            var linkIds = workItemChangedEvent.AllChangedLinkIds();

            foreach (var linkId in linkIds)
            {
                var workItem = workItemStore.GetWorkItem(linkId);

                var rules = allTransitionRules.GetAllSyncRules(workItem.Type.Name);

                if (rules.Count() == 0)
                {
                    continue;
                }

                this.updater.Synchronise(workItem, rules);
            }
        }

        /// <summary>
        /// Processes the matched rules.
        /// </summary>
        /// <param name="workItemChangedEvent">The work item changed event.</param>
        /// <param name="workItemStore">The work item store.</param>
        /// <param name="matchedRules">The matched rules.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1", Justification = "Validated in assert method")]
        protected override void ProcessMatchedRules(WorkItemChangedEvent workItemChangedEvent, WorkItemStore workItemStore, IEnumerable<TransitionRule> matchedRules)
        {
            AssertEventAndStoreAreNotNull(workItemChangedEvent, workItemStore); 
            
            var workItemId = workItemChangedEvent.GetWholeNumberFieldValue(ConstantStrings.WorkItemIdCoreField);
            var sourceWorkItem = workItemStore.GetWorkItem(workItemId);

            var workItemRuleMap = new Dictionary<int, List<TransitionRule>>();

            var selfRules = matchedRules.Where(r => r.LinkType.Target == LinkOption.Self).ToList();

            if (selfRules.Count() != 0)
            {
                workItemRuleMap.Add(workItemId, selfRules);
            }

            foreach (var rule in matchedRules.Where(r => r.LinkType.Target == LinkOption.LinkedItem))
            {
                var targetWorkItems = sourceWorkItem.GetLinkedItems(rule.LinkType.Name, rule.WorkItemTypeName.Target);

                foreach (var target in targetWorkItems)
                {
                    List<TransitionRule> workItemRuleList;
                    if (!workItemRuleMap.TryGetValue(target.Id, out workItemRuleList))
                    {
                        workItemRuleList = new List<TransitionRule>();

                        workItemRuleMap.Add(target.Id, workItemRuleList);
                    }

                    workItemRuleList.Add(rule);
                }
            }

            foreach (var workItemRules in workItemRuleMap)
            {
                workItemStore.RefreshCache();
                this.updater.Execute(workItemStore.GetWorkItem(workItemRules.Key), sourceWorkItem, workItemRules.Value);
            }
        }
    }
}